package tech

import (
	"slices"
	"ssp/set"
)

func (s *Solve) CounterDR4(g *Grid) (used bool, results []*Result) {
	used, results = g.CounterDR(g.cdr4)
	return
}

func (g *Grid) cdr4(rect []*Cell) (used bool, results []*Result) {
	rects := [][]*Cell{rect,
		{rect[1], rect[0], rect[3], rect[2]},
		{rect[2], rect[3], rect[0], rect[1]},
		{rect[3], rect[2], rect[1], rect[0]},
	}
	//solved := 0
	//for _, cell := range rect {
	//	if cell.solved() {
	//		solved++
	//	}
	//}
	//if solved > 1 {
	//	return
	//}
	ok, dr := g.newDeathRect(rect)
	if !ok {
		return
	}
	for _, rect := range rects {
		//fmt.Println("!!!", rect)
		if ok, rs := dr.cdr4unique(rect); ok {
			used = true
			results = append(results, rs...)
		}
	}
	return
}

type DeathRect struct {
	rect    []*Cell
	cands   set.Set
	links   map[[5]int]bool
	xyCands []set.Set
	//xyCells []*Cell
}

func (g *Grid) newDeathRect(rect []*Cell) (ok bool, dr *DeathRect) {
	dr = &DeathRect{
		rect:    rect,
		links:   make(map[[5]int]bool),
		cands:   set.NewSet(),
		xyCands: []set.Set{},
	}
	zCellss := [][]*Cell{
		{rect[0], rect[1]},
		{rect[1], rect[3]},
		{rect[3], rect[2]},
		{rect[2], rect[0]},
	}
	for _, zCells := range zCellss {
		house := g.rows[zCells[0].rowNum]
		if zCells[0].colNum == zCells[1].colNum {
			house = g.cols[zCells[0].colNum]
		}
		for _, cand := range zCells[0].cands.Intersect(zCells[1].cands).GetList() {
			if onlyCellsHasCand(cand, house, zCells) {
				dr.addStrongLink(cand, zCells)
				dr.cands.Add(cand)
			}
		}
	}
	for _, cell := range rect {
		if cell.solved() {
			dr.cands.Add(toCand(cell.value))
		}
	}
	//fmt.Println("newDeathRect", dr.cands)
	switch dr.cands.Count() {
	case 1:
		switch len(dr.links) {
		case 0:
			return
		case 1:
			for _, xyCells := range combine(rect, 2) {
				if xyCells[0].cands.Count() == 2 && xyCells[0].cands == xyCells[1].cands {
					//dr.xyCells = xyCells
					dr.xyCands = append(dr.xyCands, xyCells[0].cands)
					ok = true
					return
				}
			}
		default:
			for _, cell := range rect {
				if cell.cands.Count() == 2 && cell.cands.Intersect(dr.cands).Count() == 1 {
					//dr.xyCells = append(dr.xyCells, cell)
					if !slices.Contains(dr.xyCands, cell.cands) {
						dr.xyCands = append(dr.xyCands, cell.cands)
						ok = true
					}
				}
			}
			return
		}
	case 2:
		dr.xyCands = append(dr.xyCands, dr.cands)
		/*
			for _, cell := range rect {
				if cell.cands == dr.cands {
					dr.xyCells = append(dr.xyCells, cell)
				}
			}
		*/
		ok = true
		return
	default:
		return
	}
	return
}

func (r *DeathRect) isStrongLink(cand int, cells []*Cell) bool {
	key1 := [5]int{cand, cells[0].rowNum, cells[0].colNum, cells[1].rowNum, cells[1].colNum}
	key2 := [5]int{cand, cells[1].rowNum, cells[1].colNum, cells[0].rowNum, cells[0].colNum}
	return r.links[key1] || r.links[key2]
}

func (r *DeathRect) addStrongLink(cand int, cells []*Cell) {
	key := [5]int{cand, cells[0].rowNum, cells[0].colNum, cells[1].rowNum, cells[1].colNum}
	r.links[key] = true
}

/*
	func (r *DeathRect) getCandCount() int {
		return r.cands.Count()
	}

	func (r *DeathRect) getXY() set.Set {
		return r.cands
	}
*/
func (r *DeathRect) verifyB(x, y int, c0, c1, c2, c3 *Cell) (ok bool, links []string) {
	if c1.solved() {
		ok = true
		return
	}
	xyCands := set.NewSet(x, y)
	if c1.cands == xyCands {
		ok = true
		return
	}

	if r.isStrongLink(x, []*Cell{c0, c1}) {
		links = append(links, strStrongLink(x, []*Cell{c0, c1}))
		ok = true
		return
	}

	if c2.cands == xyCands &&
		r.isStrongLink(x, []*Cell{c3, c1}) {
		links = append(links, strStrongLink(x, []*Cell{c3, c1}))
		ok = true
		return
	}

	if r.isStrongLink(y, []*Cell{c2, c3}) &&
		r.isStrongLink(x, []*Cell{c3, c1}) {
		links = append(links, strStrongLink(y, []*Cell{c3, c2}))
		links = append(links, strStrongLink(x, []*Cell{c3, c1}))
		ok = true
		return
	}

	return
}
func (r *DeathRect) verifyC(x, y int, c0, c1, c2, c3 *Cell) (ok bool, links []string) {
	c1, c2 = c2, c1
	return r.verifyB(x, y, c0, c1, c2, c3)
}
func (r *DeathRect) verifyD(x, y int, c0, c1, c2, c3 *Cell) (ok bool, links []string) {
	if c3.solved() {
		ok = true
		return
	}

	xyCands := set.NewSet(x, y)
	if c3.cands == xyCands {
		ok = true
		return
	}
	if r.isStrongLink(y, []*Cell{c1, c3}) {
		links = append(links, strStrongLink(y, []*Cell{c1, c3}))
		ok = true
		return
	}
	if r.isStrongLink(y, []*Cell{c2, c3}) {
		links = append(links, strStrongLink(y, []*Cell{c2, c3}))
		ok = true
		return
	}

	return
}
func (r *DeathRect) cdr4unique(rect []*Cell) (used bool, results []*Result) {
	c0, c1, c2, c3 := rect[0], rect[1], rect[2], rect[3]
	for _, xyCands := range r.xyCands {
		if c0.cands == xyCands {
			return
		}
		x0, y0 := xyCands.GetList()[0], xyCands.GetList()[1]

		for _, xy := range [][]int{{x0, y0}, {y0, x0}} {
			x, y := xy[0], xy[1]
			if !c0.cands.Has(y) {
				continue
			}
			okb, links := r.verifyB(x, y, c0, c1, c2, c3)
			if !okb {
				continue
			}
			okc, linksc := r.verifyC(x, y, c0, c1, c2, c3)
			if !okc {
				continue
			}
			okd, linksd := r.verifyD(x, y, c0, c1, c2, c3)
			if !okd {
				continue
			}
			links = append(links, linksc...)
			links = append(links, linksd...)
			used = true
			result := new(Result)
			rmCand := set.NewSet(y)
			result.addRemoved(rmCand, []*Cell{c0})
			g1 := result.getHelpCandsGroup()
			g1.add(xyCands.Diff(rmCand), []*Cell{c0})
			for _, cell := range []*Cell{c1, c2, c3} {
				if cell.solved() {
					result.addHelpValues([]*Cell{cell})
				} else {
					g1.add(xyCands, []*Cell{cell})
				}
			}
			result.HelpChains = links
			result.setStrategyName("Counter Death Rectangle 4")
			results = append(results, result)
		}

	}

	return
}
